/*
 * Copyright 2012 Andrea Parodi
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package eban.storebyrules;

import com.google.common.base.Joiner;
import eban.storebyrules.filters.AbstractFilter;
import eban.storebyrules.filters.Filter;

import java.text.MessageFormat;
import java.util.Map;
import java.util.Set;

/**
 * Author: parroit
 * Created: 10/05/12 10.01
 */
public class StorageLanguageBuilder<TBean> {
    private PersistentCollectionImpl<TBean> persistentCollectionImpl;

    public StorageLanguageBuilder(PersistentCollectionImpl<TBean> persistentCollectionImpl) {
        this.persistentCollectionImpl = persistentCollectionImpl;
    }

    public String selectCommand(AbstractFilter filter) {
        StringBuilder builder = new StringBuilder("SELECT * FROM ");
        appendTableName(builder);
        if (filter != null)
            appendWhereFilter(filter, builder);
        builder.append(" ORDER BY " + persistentCollectionImpl.keyName()).toString();
        return builder.toString();

    }

    private void appendWhereFilter(AbstractFilter filter, StringBuilder builder) {
        builder.append(" WHERE ");
        builder.append(filter.defaultSQlClause());
    }

    private void appendTableName(StringBuilder builder) {
        builder.append(getTableName(persistentCollectionImpl.getClazz()));
    }


    public String deleteCommand(TBean instance) {
        StringBuilder builder = new StringBuilder("DELETE FROM ");
        appendTableName(builder);
        appendIndentityCriteria(instance, builder);
        return builder.toString();
    }

    private void appendIndentityCriteria(TBean instance, StringBuilder builder) {
        appendWhereFilter(Filter.eq(persistentCollectionImpl.keyName(), persistentCollectionImpl.keyValue(instance)), builder);
    }

    interface FieldAppender {
        void append(StringBuilder builder,Map.Entry<String, String> field);
    }

    public String insertCommand(TBean instance) {
        String sql;

        Map<String, String> fields = persistentCollectionImpl.getFieldsMap(instance);
        fields.put(persistentCollectionImpl.keyName(), persistentCollectionImpl.keyValue(instance));

        StringBuilder builder = new StringBuilder("INSERT INTO ");
        appendTableName(builder);
        builder.append(" ( ");
        boolean firstField = true;


        appendInFieldSet(fields, builder, new FieldAppender() {
            @Override public void append(StringBuilder builder,Map.Entry<String, String> field) {
                builder.append(field.getKey());
            }
        });


        builder.append(" ) VALUES ( ");

        appendInFieldSet(fields, builder, new FieldAppender() {
            @Override public void append(StringBuilder builder,Map.Entry<String, String> field) {
                builder.append(field.getValue());
            }
        });

        builder.append(" )");
        return builder.toString();
    }

    private void appendInFieldSet(Map<String, String> fields, final StringBuilder builder,FieldAppender fieldAppender) {
        boolean firstField=true;
        for (final Map.Entry<String, String> field : fields.entrySet()) {
            if (!firstField)
                builder.append(",");
            firstField = false;

            fieldAppender.append(builder,field);

        }
    }

    public String retrieveAutoKeyCommand() {
        return "select max(" + persistentCollectionImpl.keyName() + ") as " + persistentCollectionImpl.keyName() + " from " + getTableName(persistentCollectionImpl.getClazz());
    }

    public String updateCommand(TBean instance, Object keyValue) {

        StringBuilder builder=new StringBuilder("UPDATE ");
        appendTableName(builder);
        builder.append(" SET ");

        appendInFieldSet(persistentCollectionImpl.getFieldsMap(instance), builder, new FieldAppender() {
            @Override public void append(StringBuilder builder,Map.Entry<String, String> field) {
                builder.append(field.getKey() + " = " + field.getValue());
            }
        });

        appendIndentityCriteria(instance,builder);
        return builder.toString();
    }

    public String createTableCommand(TBean instance) {
        final String pattern;


        final Set<String> allFields = persistentCollectionImpl.getFieldsMap(instance).keySet();
        if (allFields.size() > 0)
            pattern = "create table {0}(\r\n{1} varchar(2000) primary key, \r\n{2} varchar(2000) \r\n);";
        else
            pattern = "create table {0}(\r\n{1} varchar(2000) primary key\r\n);";

        return MessageFormat.format(pattern,
                getTableName(persistentCollectionImpl.getClazz()),
                persistentCollectionImpl.keyName(),
                Joiner.on(" varchar(2000),\r\n").join(allFields)
        );
    }

    private String tableNameCached;
    public String getTableName(Class<?> theClass) {
        if (tableNameCached==null){
            String tableName = theClass.getSimpleName();
            TableName tb = theClass.getAnnotation(TableName.class);
            if (tb != null)
                tableName = tb.value();
            tableNameCached=tableName;

        }
        return tableNameCached;
    }
}
